/************************************************************************

								Heroes IV
					  Copyright 2001, The 3DO Company

	------------------------------------------------------------------
  						    dialog_choose_lord.cpp

	$Header: $

	$NoKeywords: $

 ************************************************************************/

// kill the 255+ character identifier warning.  Templates tend to exceed that easily.
#include "precompiled.h"
#pragma warning( disable: 4786 )

#include "dialog_choose_lord.h"

#include <algorithm>

#include "abstract_town.h"
#include "adaptor_handler.h"
#include "bitmap_group_cache.h"
#include "bitmap_layer_cache_window.h"
#include "bitmap_layer_window.h"
#include "bound_handler.h"
#include "button.h"
#include "button_cache.h"
#include "create_creature_button.h"
#include "external_string.h"
#include "format_string.h"
#include "hero.h"
#include "hero_keyword_replacer.h"
#include "interface_help.h"
#include "item_help_block.h" 
#include "replace_keywords.h"
#include "screen_point.h"
#include "scrollbar.h"
#include "simple_dialog.h"
#include "skill.h"
#include "skill_properties.h"
#include "standard_fonts.h"
#include "text_window.h"
#include "toggle_button.h"

extern t_button_cache g_ok_button;
extern t_button_cache g_cancel_button;
extern t_bitmap_group_cache const k_creature_select_bitmaps;

extern t_external_string const k_choose_lord_title("choose_lord.title");
static t_external_string const k_choose_lord_text("choose_lord.text");

static t_external_string const k_selection_suceeded_text("lord_selection_suceeded.text");
static t_external_string const k_selection_failed_text("lord_selection_failed.text");

namespace 
{
	t_bitmap_group_cache const k_bitmaps( "dialog.choose_lord" );


	// ---------------------------------------------------------------------------
	// Predicate to sort a std::vector< t_hero * > by nobility skill
	// ---------------------------------------------------------------------------
	class t_nobility_sorting_predicate
	{
		public:
			t_nobility_sorting_predicate( t_abstract_town * town );

			bool operator()( t_hero * hero1, t_hero * hero2 );

		private:
			t_abstract_town * const m_town;
	};

	inline t_nobility_sorting_predicate::t_nobility_sorting_predicate( t_abstract_town * town )
		: m_town( town )
	{
	}

	inline bool t_nobility_sorting_predicate::operator()( t_hero * hero1, t_hero * hero2 )
	{
		int power1 = hero1->get_skill_power( k_skill_nobility );
		int power2 = hero2->get_skill_power( k_skill_nobility );
		if ( power1 > power2 )
			return true;
		else if ( power1 < power2 )
			return false;

		// List heroes attached to this town first so that the current governer is
		// always at the top
		if ( hero1->get_nobility_town().get() == m_town )
		{
			if ( hero2->get_nobility_town().get() != m_town )
			{
				return true;
			}
		}
		else
		{
			if ( hero2->get_nobility_town().get() == m_town )
			{
				return false;
			}
		}

		return hero1->get_nobility_priority() > hero2->get_nobility_priority();
	}
}

// ---------------------------------------------------------------------------
// ---------------------------------------------------------------------------
t_dialog_choose_lord::t_dialog_choose_lord( std::vector< t_hero * > const & lords,
											t_abstract_town *       town,
											t_window *              parent )
	: t_window( k_completely_transparent, parent ),
	  m_heroes( lords ),
	  m_town( town )
{
	// Sort heroes by nobility skill
	t_nobility_sorting_predicate nobility_sorting_predicate( town );

	std::sort( m_heroes.begin(), m_heroes.end(), nobility_sorting_predicate );

	m_current_selection = 0;

	t_help_block const&		help = get_help_block( "choose_lord" );

	// Create subwindows
	m_bitmaps = k_bitmaps.get();

	t_bitmap_layer const * layer;
	t_window *             background;
	t_button *			   button;
	t_text_window *		   text_window;

	layer = m_bitmaps->find( "background" );
	background = new t_bitmap_layer_window( layer, t_screen_point(0, 0), this );

	layer = m_bitmaps->find( "title" );
	text_window = new t_text_window( get_font( 20 ), layer->get_rect(), background, k_choose_lord_title, t_pixel_24(0,0,0) );
	text_window->set_drop_shadow( false );
	text_window->set_justification( k_justify_center );

	layer = m_bitmaps->find( "text" );
	text_window = new t_text_window( get_font( 18 ), layer->get_rect(), background, replace_keywords( k_choose_lord_text, "%town_name", m_town->get_name() ), t_pixel_24(0,0,0) );
	text_window->set_drop_shadow( false );

	layer = m_bitmaps->find( "ok_button" );
	button = new t_button( g_ok_button.get(), layer->get_rect().top_left(), this );
	button->set_click_handler( bound_handler( *this, &t_dialog_choose_lord::ok_clicked ) );
  set_help( button, help, "ok" );

	layer = m_bitmaps->find( "cancel_button" );
	button = new t_button( g_cancel_button.get(), layer->get_rect().top_left(), this );
	button->set_click_handler( bound_handler( *this, &t_dialog_choose_lord::cancel_clicked ) );
  set_help( button, help, "cancel" );

	layer = m_bitmaps->find( "scrollbar" );
	m_scrollbar = new t_scrollbar( layer->get_rect().top_left(), layer->get_rect().height(),
								   this, 0, m_heroes.size() - k_displayed_heroes );
	m_scrollbar->set_handler( bound_handler( *this, &t_dialog_choose_lord::scrollbar_change ) );
	m_scrollbar->set_page_size( k_displayed_heroes );
	m_scrollbar->set_visible( m_heroes.size() > k_displayed_heroes );

	// Determine in-hero layout
	layer = m_bitmaps->find( "hero_1" );
	t_screen_point hero_1_top_left = layer->get_rect().top_left();

	layer = m_bitmaps->find( "hero_portrait" );
	t_screen_rect portrait_offset = layer->get_rect() - hero_1_top_left;

	layer = m_bitmaps->find( "hero_name" );
	t_screen_rect name_offset = layer->get_rect() - hero_1_top_left;

	layer = m_bitmaps->find( "nobility_skill" );
	t_screen_rect skill_offset = layer->get_rect() - hero_1_top_left;

	layer = m_bitmaps->find( "town_name" );
	t_screen_rect town_offset = layer->get_rect() - hero_1_top_left;

	// Create hero info
	int display_count = k_displayed_heroes;

	if ( display_count > m_heroes.size() )
		display_count = m_heroes.size();

	int i;

	for ( i = 0; i < display_count; i++ )
	{
		t_screen_point hero_top_left;
		
		if ( i == 0 )
		{
			hero_top_left = hero_1_top_left;
		}
		else
		{
			layer = m_bitmaps->find( format_string( "hero_%d", i + 1) );
			hero_top_left = layer->get_rect().top_left();
		}
		
		m_hero_display[i].hero_index = i;

		layer = m_bitmaps->find( "text_background" );
		new t_bitmap_layer_window( layer, hero_top_left - hero_1_top_left, this );

		m_hero_display[i].hero_button = new t_toggle_button( hero_top_left + portrait_offset.top_left(), this, true );
		m_hero_display[i].hero_button->set_click_handler( add_2nd_argument( bound_handler( *this, &t_dialog_choose_lord::hero_clicked ), i ) );
	    set_help( m_hero_display[i].hero_button, help, "portrait" );

		create_creature_button( m_hero_display[i].hero_button.get(), k_creature_select_bitmaps.get(),
								m_heroes[i]->get_portrait(), "", false,
								&m_hero_display[i].hero_portrait_images );

		m_hero_display[i].hero_name = new t_text_window( get_font( 16 ), hero_top_left + name_offset, this, "", t_pixel_24(0,0,0) );
		m_hero_display[i].hero_name->set_drop_shadow( false );
		m_hero_display[i].hero_name->set_justification( k_justify_center );
	    set_help( m_hero_display[i].hero_name, help, "name" );

		m_hero_display[i].hero_skill = new t_text_window( get_font( 16 ), hero_top_left + skill_offset, this, "", t_pixel_24(0,0,0) );
		m_hero_display[i].hero_skill->set_drop_shadow( false );
		m_hero_display[i].hero_skill->set_justification( k_justify_center );
	    set_help( m_hero_display[i].hero_skill, help, "skill_level" );

		m_hero_display[i].current_town = new t_text_window( get_font( 16 ), hero_top_left + town_offset, this, "", t_pixel_24(0,0,0) );
		m_hero_display[i].current_town->set_drop_shadow( false );
		m_hero_display[i].current_town->set_justification( k_justify_center );
	    set_help( m_hero_display[i].current_town, help, "current_town" );
	}

	for ( ; i < k_displayed_heroes; i++ )
	{
		m_hero_display[i].hero_index = -1;
	}

	t_screen_rect parent_rect = parent->get_client_rect();
    
	t_screen_rect rect = m_bitmaps->get_rect();
	rect += (parent_rect.size() - rect.size()) / 2;
	init( rect );

	update_all_displays();
}

// ---------------------------------------------------------------------------
// Handle a click of the cancel button
// ---------------------------------------------------------------------------
void t_dialog_choose_lord::cancel_clicked( t_button * )
{
	m_return_value = false;
	close();
}

// ---------------------------------------------------------------------------
// Handle a click in the hero portrait ( selects the hero )
// ---------------------------------------------------------------------------
void t_dialog_choose_lord::hero_clicked( t_button *, int display_index )
{
	int old_selection = m_current_selection;
	m_current_selection = m_hero_display[display_index].hero_index;

	int old_display_index = old_selection - m_hero_display[0].hero_index;

	if ( old_display_index >= 0 && old_display_index < k_displayed_heroes )
		update_display( old_display_index );

	update_display( display_index );
}

// ---------------------------------------------------------------------------
// Handle a click of the OK button. Assign hero to this town & close window
// ---------------------------------------------------------------------------
void t_dialog_choose_lord::ok_clicked( t_button * )
{
	t_hero * selected_hero = m_heroes[m_current_selection];

	selected_hero->set_nobility_town( m_town );
	selected_hero->raise_nobility_priority();

	t_hero_keyword_replacer replacer;

	replacer.add_hero( selected_hero );
	replacer.add_keyword( "%town_name", m_town->get_name() );

	t_hero * best_hero = m_town->get_best_nobility_hero();
	if ( best_hero == selected_hero )
	{
		ok_dialog( replacer( k_selection_suceeded_text ), true );
	}
	else
	{
		replacer.add_keyword( "%governer", best_hero->get_name() );
		ok_dialog( replacer( k_selection_failed_text ), true );
	}

	m_return_value = true;
	close();
}

// ---------------------------------------------------------------------------
// Handle a change in the scrollbar position
// ---------------------------------------------------------------------------
void t_dialog_choose_lord::scrollbar_change( t_scrollbar *, int position )
{
	int i;
	for ( i = 0; i < k_displayed_heroes; i++ )
	{
		// Should only happen when there are more than 4 heroes...
		m_hero_display[i].hero_index = position + i;
		update_display( i );
	}
}

// ---------------------------------------------------------------------------
// Update all 4 hero display windows
// ---------------------------------------------------------------------------
void t_dialog_choose_lord::update_all_displays()
{
	int i;
	for ( i = 0; i < k_displayed_heroes; i++ )
	{
		update_display( i );
	}
}

// ---------------------------------------------------------------------------
// Update the given hero display window
// ---------------------------------------------------------------------------
void t_dialog_choose_lord::update_display( int display_index )
{
	assert( display_index >= 0 && display_index < k_displayed_heroes );

	if ( m_hero_display[display_index].hero_index == -1 )
	{
		assert( display_index >= m_heroes.size() );
		return;
	}


	assert( m_hero_display[display_index].hero_index >= 0 && m_hero_display[display_index].hero_index < m_heroes.size() );

	// Just in case
	m_hero_display[display_index].current_town->set_visible( true );
	m_hero_display[display_index].hero_name->set_visible( true );
	m_hero_display[display_index].hero_skill->set_visible( true );
	m_hero_display[display_index].hero_button->set_visible( true );

	t_hero * hero = m_heroes[m_hero_display[display_index].hero_index];
	m_hero_display[display_index].hero_name->set_text( hero->get_name() );
	m_hero_display[display_index].hero_skill->set_text( get_name( t_skill( k_skill_nobility, hero->get_skill( k_skill_nobility ) ) ) );

	t_abstract_town_ptr nobility_town = hero->get_nobility_town();
	if ( nobility_town.get() )
	{
		m_hero_display[display_index].current_town->set_text( nobility_town->get_name() );
	}
	else
	{
		m_hero_display[display_index].current_town->set_visible( false );
	}

	// Change portrait
	std::list<t_bitmap_layer_cache_window_ptr>::iterator window_iterator;

	for ( window_iterator = m_hero_display[display_index].hero_portrait_images.begin(); window_iterator != m_hero_display[display_index].hero_portrait_images.end(); window_iterator++ )
	{
		(*window_iterator)->set_bitmap( hero->get_portrait() );
	}

	m_hero_display[display_index].hero_button->set_pressed( m_current_selection == m_hero_display[display_index].hero_index );

	t_help_block const&		help = get_help_block( "choose_lord" );
	t_hero_keyword_replacer replacer;
	replacer.add_hero( hero );

	m_hero_display[display_index].hero_button->set_help_balloon_text( replacer( help.get_name( "portrait" ) ) );
	m_hero_display[display_index].hero_button->set_right_click_text( replacer( help.get_help( "portrait" ) ) );

	m_hero_display[display_index].hero_button->invalidate();
}

